﻿using NetDimension.NanUI.ResourceHandler;
using System;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace NetDimension.NanUI
{
    public class ZippedResourceHandler : ResourceHandlerBase
    {
        private Func<string, string> OnFallback { get; }
        private string[] DefaultFileName { get; set; } = new string[] { "index.html" };

        private string ZipFilePath { get; set; }
        private bool EnableCache { get; set; }
        private string RootPath { get; set; }
        private static byte[] ZipFileCache { get; set; }

        public ZippedResourceHandler(string zipFilePath, bool enableCache, string rootPath = "")
        {
            this.ZipFilePath = zipFilePath;
            this.EnableCache = enableCache;
            this.RootPath = rootPath;
            if (EnableCache && ZipFileCache == null)
            {
                ZipFileCache = SetZipFileBuffer(ZipFilePath);
            }
        }

        protected override FormiumResponse GetResponse(FormiumRequest request)
        {
            FileStreamResponse response = new FileStreamResponse(request, true);
            if (request.Method != Method.GET)
            {
                response.Status = (int)System.Net.HttpStatusCode.NotFound;
                return response;
            }

            var filePath = this.RootPath + response.RelativePath.Replace("//", "/");

            dynamic mstream;
            if (EnableCache)
                mstream = new MemoryStream(ZipFileCache);
            else
                mstream = new FileStream(ZipFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);

            using (var archive = new ZipArchive(mstream, ZipArchiveMode.Read))
            {
                var fileEntry = archive.GetEntry(filePath);

                if (fileEntry == null && string.IsNullOrEmpty(response.FileName))
                {
                    foreach (var defaultFileName in DefaultFileName)
                    {
                        var searchPath = string.Join("/", filePath.TrimEnd('/'), defaultFileName).TrimStart('/');
                        fileEntry = archive.GetEntry(searchPath);
                        if (fileEntry != null)
                        {
                            response.MimeType = GetMimeType(defaultFileName);
                            break;
                        }
                    }
                }

                if (fileEntry == null && OnFallback != null)
                {
                    var fallbackFile = OnFallback.Invoke(filePath);
                    fileEntry = archive.GetEntry(fallbackFile);
                    if (fileEntry != null)
                    {
                        response.MimeType = GetMimeType(fallbackFile);
                    }
                }

                if (fileEntry != null)
                {
                    using (var entryStream = fileEntry.Open())
                    {
                        var fileStream = new MemoryStream();
                        entryStream.CopyTo(fileStream);
                        response.ContentStream = fileStream;
                        entryStream.Close();
                    }
                }
                else
                {
                    response.Status = (int)System.Net.HttpStatusCode.NotFound;
                    //response.ContentStream = TxtToMStream("Page does not exist.  Url: "+ filePath);
                }

                mstream.Close();
                mstream.Dispose();
            }

            return response;
        }

        protected string GetMimeType(string fileName)
        {
            var ext = Path.GetExtension(fileName)?.Trim('.');
            if (string.IsNullOrEmpty(ext))
            {
                return "application/octet-stream";
            }
            return MimeMapping.GetMimeMapping(fileName);
        }

        protected byte[] SetZipFileBuffer(string zipPath)
        {
            byte[] byt = null;
            using (var stream = new FileStream(zipPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (var mstream = new MemoryStream())
            {
                stream.CopyTo(mstream);
                stream.Dispose();
                byt = mstream.ToArray();
            }
            return byt;
        }

        protected MemoryStream TxtToMStream(string content)
        {
            var mstream = new MemoryStream();
            var byt = System.Text.Encoding.UTF8.GetBytes(content);
            return new MemoryStream(byt);
        }
    }
}
